﻿using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Diagnostics;
using Avalonia.Controls.Primitives;
using Avalonia.Input;
using Avalonia.Media;
using LightCAD.Core;
using LightCAD.Runtime;
using LightCAD.Runtime.Creator;
using LightCAD.Runtime.Editor;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using SkiaSharp;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace LightCAD.UI
{

    public sealed class VPort
    {

        public static CursorAddonType CursorAddon { get; set; }

        public static SKColor SelectRectFillColor_Included = new SKColor(0x80003B93);
        public static SKColor SelectRectFillColor_Crossed = new SKColor(0x80008217);
        public static float HoverTimeSpan = 0.5f;//悬停判别时间
        public static SKColor Background = new SKColor(0xff212830);
        public static double ZoomRatio = 0.8;

        public static SKColor GridColor = new SKColor(0xff343A4B);
        public static SKColor SubGridColor = new SKColor(0xff282E3A);
        public static float GridSize = 5f;
        public static float GridRatio = 5;

        public static SKColor OriginAxisColor = new SKColor(0xffffffff);
        public static SKColor AxisYColor = new SKColor(0xff307F39);
        public static SKColor AxisXColor = new SKColor(0xff5B0808);
        public static float OriginLength = 100.0f;
        public static float OriginSize = 12.0f;
        public static float OriginX = 100;
        public static float OriginY = 100;
        public static double MinSize = 0.001;
        public static double MaxSize = 1E+12;

        public static double Zoom = 1.0;
        public static Box2d WorldScope;
        public static SKRect ScrScope;

        public static SKMatrix ViewMatrix = SKMatrix.Identity;

        public static bool IsDragging = false;
        public static bool IsDraggingGrip = false;
        public static bool IsCreating { get; set; }
        public static ElementCreator CurrentCreator { get; set; }

        public static bool IsPan { get; set; }
        public static Box2d PanScope;
        public static bool IsSelecting { get; set; }
        public static bool IsPointInputing { get; internal set; }
        public static bool ViewNeedUpdate { get; internal set; } = true;
        public static int DrawCounter { get; internal set; }
        public static bool IsPointerHovering { get; internal set; }

        public static SKPoint PointerPressedPosition;
        public static SKPoint PointerReleasedPosition;
        public static SKPoint PointerMovedPosition;
        public static SKPoint PointerWheelPosition;


        public static float CursorSize = 50;
        public static float SelectBoxSize = 3;

        public static IPopupHost HoveredElementTip;
        public static DrawingEditor DrawingControl;

        /// <summary>
        /// 获取当前十字光标位置
        /// </summary>
        /// <returns></returns>
        public static SKPoint GetCrossCursorPosition()
        {
            return new SKPoint();
        }

        public static double ScrToWorld(float length)
        {
            var zoom = ScrScope.Width / WorldScope.Width;
            return (1 / zoom) * length;
        }
        public static Vector2d ScrToWorld(SKPoint sp)
        {
            var sptmp = new Vector2d(sp.X, ScrScope.Height - sp.Y);
            var zoom = ScrScope.Width / WorldScope.Width;
            var wptmp = (1 / zoom) * sptmp;
            var mt = Matrix3.Translate(WorldScope.Location);
            var wp = mt * wptmp;
            return wp;

        }
        public static float WorldToScr(double length)
        {
            var zoom = ScrScope.Width / WorldScope.Width;
            return (float)(zoom * length);
        }
        public static SKMatrix WorldToScrMatrix()
        {
            var zoom = ScrScope.Width / WorldScope.Width;
            var scrMatrix = SKMatrix.Identity;
            scrMatrix.ScaleY = -(float)zoom;
            scrMatrix.ScaleX = (float)zoom;
            scrMatrix.TransY = ScrScope.Height;//bound height

            var transMatrix = SKMatrix.CreateTranslation((float)WorldScope.Location.X, (float)WorldScope.Location.Y).Invert();
            var totalMatrix = SKMatrix.Concat(scrMatrix, transMatrix);
            return totalMatrix;
        }
        public static SKPoint WorldToScr(Vector2d wp)
        {
            //var matrix = WorldToScrMatrix();
            //return matrix.MapPoint((float)wp.X,(float)wp.Y);

            var mt = Matrix3.Translate(WorldScope.Location).inverse;
            var tptmp = mt * wp;
            var zoom = ScrScope.Width / WorldScope.Width;
            var sptmp = (zoom * tptmp);
            var sp = new SKPoint((float)sptmp.X, (float)(ScrScope.Height - sptmp.Y));
            return sp;
        }
        public static void InitViewScope()
        {
            WorldScope.Location = new Vector2d(-OriginX, -OriginY);
            WorldScope.Width = ScrScope.Width;
            WorldScope.Height = ScrScope.Height;
            //VPort.WorldToScrMatrix();
        }

        public static void DrawOriginAxis(SKCanvas canvas)
        {
            using (var paint = new SKPaint { Color = OriginAxisColor, IsStroke = true })
            {
                var x = (OriginX - OriginSize / 2);
                var y = (ScrScope.Height - OriginY - OriginSize / 2);
                var w = OriginSize - 1;
                var h = OriginSize - 1;
                canvas.DrawRect(x, y, w, h, paint);
                canvas.DrawLine(OriginX, ScrScope.Height - OriginY, OriginX, ScrScope.Height - OriginY - OriginLength, paint);
                canvas.DrawLine(OriginX, ScrScope.Height - OriginY, OriginX + OriginLength, ScrScope.Height - OriginY, paint);
            }
        }
        public static void DrawGrid(SKCanvas canvas)
        {
            using (var g_paint = new SKPaint { Color = GridColor, IsStroke = true })
            using (var ay_paint = new SKPaint { Color = AxisYColor, IsStroke = true })
            using (var ax_paint = new SKPaint { Color = AxisXColor, IsStroke = true })
            using (var sg_paint = new SKPaint { Color = SubGridColor, IsStroke = true })
            {
                //绘制竖向Grid 原点左边
                var idx = 0;
                while (OriginX - idx * GridSize * GridRatio > 0)
                {
                    var x = OriginX - idx * GridSize * GridRatio;
                    if (x == OriginX)
                    {
                        //绘制Y正轴
                        canvas.DrawLine(x, 0, x, ScrScope.Height - OriginY, ay_paint);
                        //负轴方向绘制Grid
                        canvas.DrawLine(x, ScrScope.Height - OriginY, x, ScrScope.Height, g_paint);
                    }
                    else
                    {
                        if (idx % 5 == 0)
                            canvas.DrawLine(x, 0, x, ScrScope.Height - 1, g_paint);
                        else
                            canvas.DrawLine(x, 0, x, ScrScope.Height - 1, sg_paint);
                    }
                    idx++;
                }

                //绘制竖向Grid 原点右边
                idx = 0;
                while (OriginX + idx * GridSize * GridRatio <= ScrScope.Width - 1)
                {
                    var x = OriginX + idx * GridSize * GridRatio;
                    if (x == OriginX)
                    {
                        idx++;
                        continue;
                    }
                    else
                    {
                        if (idx % 5 == 0)
                            canvas.DrawLine(x, 0, x, ScrScope.Height - 1, g_paint);//绘制主网格
                        else
                            canvas.DrawLine(x, 0, x, ScrScope.Height - 1, sg_paint);//绘制次网格
                    }
                    idx++;
                }

                var oY = ScrScope.Height - OriginY;
                //绘制横向Grid 原点上边
                idx = 0;
                while (oY - idx * GridSize * GridRatio > 0)
                {
                    var y = oY - idx * GridSize * GridRatio;
                    if (y == oY)
                    {
                        //绘制X正轴
                        canvas.DrawLine(OriginX, oY, ScrScope.Width - 1, oY, ax_paint);
                        //负轴方向绘制Grid
                        canvas.DrawLine(0, oY, OriginX, oY, g_paint);
                    }
                    else
                    {
                        if (idx % 5 == 0)
                            canvas.DrawLine(0, y, ScrScope.Width - 1, y, g_paint);//绘制主网格
                        else
                            canvas.DrawLine(0, y, ScrScope.Width - 1, y, sg_paint);//绘制次网格
                    }
                    idx++;
                }

                //绘制横向Grid 原点下面
                idx = 0;
                while (oY + idx * GridSize * GridRatio <= ScrScope.Height - 1)
                {
                    var y = oY + idx * GridSize * GridRatio;
                    if (y == oY)
                    {
                        idx++;
                        continue;
                    }
                    else
                    {
                        if (idx % 5 == 0)
                            canvas.DrawLine(0, y, ScrScope.Width - 1, y, g_paint);
                        else
                            canvas.DrawLine(0, y, ScrScope.Width - 1, y, sg_paint);
                    }
                    idx++;
                }
            }
        }

        public static void DrawSelectRect(SKCanvas canvas, SKPoint from, SKPoint to)
        {
            if (from == to) return;
            if (from.X > to.X)
            {
                DrawSelectRectCrossed(canvas, from, to);
            }
            else
            {
                DrawSelectRectIncluded(canvas, from, to);
            }
        }
        public static void DrawSelectRectIncluded(SKCanvas canvas, SKPoint from, SKPoint to)
        {
            using (var fillColor = new SKPaint { Color = SelectRectFillColor_Included, IsStroke = false })
            using (var penColor = new SKPaint { Color = SKColors.White, IsStroke = true })
            {
                var x = from.X;
                var y = Math.Min(from.Y, to.Y);
                var w = Math.Abs(to.X - from.X);
                var h = Math.Abs(to.Y - from.Y);
                canvas.DrawRect(x, y, w, h, fillColor);
                canvas.DrawRect(x, y, w, h, penColor);
            }
        }
        public static void DrawSelectRectCrossed(SKCanvas canvas, SKPoint from, SKPoint to)
        {
            using (var fillColor = new SKPaint { Color = SelectRectFillColor_Crossed, IsStroke = false })
            using (var penColor = new SKPaint
            {
                Color = SKColors.White,
                IsStroke = true,
                PathEffect = SKPathEffect.CreateDash(new float[] { 5, 5 }, 20)
            })
            {
                var x = Math.Min(from.X, to.X);
                var y = Math.Min(from.Y, to.Y);
                var w = Math.Abs(to.X - from.X);
                var h = Math.Abs(to.Y - from.Y);
                canvas.DrawRect(x, y, w, h, fillColor);
                canvas.DrawRect(x, y, w, h, penColor);
            }
        }
        /// <summary>
        /// 平移可视范围
        /// </summary>
        public static void PanViewScope()
        {
            var scrDx = PointerMovedPosition.X - PointerPressedPosition.X;
            var scrDy = -(PointerMovedPosition.Y - PointerPressedPosition.Y);
            if (scrDx == 0 && scrDy == 0) return;
            var zoom = WorldScope.Width / ScrScope.Width;
            var worldDx = scrDx * zoom;
            var worldDy = scrDy * zoom;
            WorldScope.Location.X = VPort.PanScope.Location.X - worldDx;
            WorldScope.Location.Y = VPort.PanScope.Location.Y - worldDy;
            var org = WorldToScr(new Vector2d());
            OriginX = org.X;
            OriginY = ScrScope.Height - org.Y;
        }
        /// <summary>
        /// 缩放可视范围
        /// </summary>
        /// <param name="deltaY"></param>
        public static void ZoomWheel(double deltaY)
        {

            if (deltaY < 0)//zoom out 缩小
            {
                VPort.GridRatio *= (float)VPort.ZoomRatio;
                VPort.Zoom *= VPort.ZoomRatio;
            }
            else //zoom in 放大
            {
                VPort.GridRatio /= (float)VPort.ZoomRatio;
                VPort.Zoom /= VPort.ZoomRatio;
            }

            if (Math.Round(VPort.GridRatio, 6) < 1)
            {
                VPort.GridRatio = 5;
            }
            if (Math.Round(VPort.GridRatio, 6) > 5)
            {
                VPort.GridRatio = 1;
            }
            var wheelp = ScrToWorld(VPort.PointerWheelPosition);
            var mt = Matrix3.Translate(wheelp).inverse;//以wp为原点的临时坐标系
            var wptmp = mt * WorldScope.Location;//将世界坐标转换到临时坐标系
            //缩小操作相当于放大视口范围,反之亦然
            var zoom = (deltaY < 0) ? (1 / VPort.ZoomRatio) : VPort.ZoomRatio;
            var zoomwp = zoom * wptmp;

            WorldScope.Location = Matrix3.Translate(wheelp) * zoomwp;
            WorldScope.Width *= zoom;
            WorldScope.Height *= zoom;
            var org = WorldToScr(new Vector2d());
            OriginX = org.X;
            OriginY = ScrScope.Height - org.Y;

        }

        public static void DrawCursor(SKCanvas canvas, LcadCursorType cursorType)
        {
            var p = PointerMovedPosition;
            using (var penColor = new SKPaint { Color = SKColors.White, IsStroke = true })
            {
                canvas.DrawLine(p.X - CursorSize, p.Y, p.X + CursorSize, p.Y, penColor);
                canvas.DrawLine(p.X, p.Y - CursorSize, p.X, p.Y + CursorSize, penColor);
                if (cursorType == LcadCursorType.SelectElement)
                {
                    canvas.DrawRect(p.X - SelectBoxSize, p.Y - SelectBoxSize, SelectBoxSize * 2, SelectBoxSize * 2, penColor);
                }
                const int CursorAddonSize = 3;
                var ap = new SKPoint(p.X + CursorAddonSize * 2, p.Y - CursorAddonSize * 2);
                if (CursorAddon == CursorAddonType.Plus)
                {
                    canvas.DrawLine(ap.X - CursorAddonSize, ap.Y, ap.X + CursorAddonSize, ap.Y, penColor);
                    canvas.DrawLine(ap.X, ap.Y - CursorAddonSize, ap.X, ap.Y + CursorAddonSize, penColor);
                }
            }
        }

        public static void DrawCursorOnFloat(SKCanvas canvas, LcadCursorType cursorType)
        {
            var p = PointerMovedPosition;
            using (var paint = new SKPaint { Color = SKColors.White,IsStroke=true })
            {
                canvas.DrawLine(new SKPoint(p.X - CursorSize, p.Y), new SKPoint(p.X + CursorSize, p.Y), paint);
                canvas.DrawLine(new SKPoint(p.X, p.Y - CursorSize), new SKPoint(p.X, p.Y + CursorSize), paint);
                if (cursorType == LcadCursorType.SelectElement)
                {
                    canvas.DrawRect(p.X - SelectBoxSize, p.Y - SelectBoxSize, SelectBoxSize * 2, SelectBoxSize * 2, paint);
                }
                const int CursorAddonSize = 3;
                var ap = new SKPoint(p.X + CursorAddonSize * 2, p.Y - CursorAddonSize * 2);
                if (CursorAddon == CursorAddonType.Plus)
                {
                    canvas.DrawLine(new SKPoint(ap.X - CursorAddonSize, ap.Y), new SKPoint(ap.X + CursorAddonSize, ap.Y), paint);
                    canvas.DrawLine(new SKPoint(ap.X, ap.Y - CursorAddonSize), new SKPoint(ap.X, ap.Y + CursorAddonSize), paint);
                }
            }
        }
        public static Element HitUnselect()
        {
            var pw = ScrToWorld(PointerPressedPosition);
            var sizew = ScrToWorld(SelectBoxSize);
            var element = DocumentScene.HitUnselect(LcDocument.Current.ModelSpace, pw, sizew);
            if (element != null) RuntimeScene.ClearElementGrips(new List<Element> { element });
            return element;

        }
        public static Tuple<Element, bool> HitSelect()
        {
            var pw = ScrToWorld(PointerPressedPosition);
            var sizew = ScrToWorld(SelectBoxSize);
            var result = DocumentScene.HitSelect(LcDocument.Current.ModelSpace, pw, sizew);
            if (result == null) return null;
            if (result.Item2 == true)//首次选中
            {
                RuntimeScene.ResetElementGrips(new List<Element> { result.Item1 });
            }
            return result;

        }
        public static List<Element> RectSelects()
        {
            var fpw = ScrToWorld(PointerPressedPosition);
            var spw = ScrToWorld(PointerReleasedPosition);
            var selectBox = new Box2d(fpw, spw);
            var elements = DocumentScene.RectSelects(LcDocument.Current.ModelSpace, selectBox, spw.X > fpw.X);
            RuntimeScene.ResetElementGrips(elements);
            return elements;
        }

        internal static void DragSelects()
        {
            var pre = ScrToWorld(PointerPressedPosition);
            var mov = ScrToWorld(PointerMovedPosition);
            DocumentScene.DragX = mov.X - pre.X;
            DocumentScene.DragY = mov.Y - pre.Y;
        }

        internal static void CancelAll()
        {
            if (IsDragging)
            {
                IsDragging = false;
                DocumentScene.SetSelectsToDragging(false);
            }
            if (IsDraggingGrip)
            {
                IsDraggingGrip = false;
            }
            else
            {
                DocumentScene.ClearSelects();
            }

        }

        internal static void StartDrag()
        {
            DocumentScene.DragX = 0;
            DocumentScene.DragY = 0;
            RuntimeScene.ClearElementGrips();
            DocumentScene.SetSelectsToDragging(true);

        }

        internal static void EndDrag(bool isControlPressed)
        {
            var pre = ScrToWorld(PointerPressedPosition);
            var mov = ScrToWorld(PointerMovedPosition);
            DocumentScene.DragX = mov.X - pre.X;
            DocumentScene.DragY = mov.Y - pre.Y;
            CursorAddon = CursorAddonType.None;
            DocumentScene.SetSelectsToDragging(false);
            if (isControlPressed)
                Commands.DragCopy(DocumentScene.DragX, DocumentScene.DragY);
            else
                Commands.Drag(DocumentScene.DragX, DocumentScene.DragY);
        }

        internal static Tuple<SKRect, Box2d> GetWorldScope()
        {
            return new Tuple<SKRect, Box2d>(ScrScope, WorldScope);
        }
        static IPopupHost _popupHost1;
        static IPopupHost _popupHost2;
        static TextBox _vportInputer;
        public static bool IsShowFloatBlock { get; set; }
        public static bool IsPasting { get; internal set; }

        internal static void ShowFloatBlock(Control control)
        {
            if (!IsShowFloatBlock)
            {
                if (_popupHost1 != null)
                {
                    _popupHost1.Hide();
                    _popupHost2.Hide();
                    _popupHost1 = null;
                    _popupHost2 = null;
                    _vportInputer = null;
                }
                return;
            }


            if (_popupHost1 != null)
            {
                _popupHost1.ConfigurePosition(control, PlacementMode.Pointer, new Point(10, 10));
                _popupHost2.ConfigurePosition(control, PlacementMode.Pointer, new Point(100, 10));
                return;
            }


            _vportInputer = new TextBox();
            _vportInputer.Text = "Input:";
            _vportInputer.Background = Brushes.Green;
            _popupHost1 = OverlayPopupHost.CreatePopupHost(control, null);
            _popupHost1.SetChild(_vportInputer);
            ((ISetLogicalParent)_popupHost1).SetParent(control);
            _popupHost1.ConfigurePosition(control, PlacementMode.Pointer, new Point(10, 10));
            _popupHost1.Show();

            var floatCtrl2 = new TextBlock();
            floatCtrl2.Text = "HELLO2";
            floatCtrl2.Background = Brushes.LightBlue;
            _popupHost2 = OverlayPopupHost.CreatePopupHost(control, null);
            _popupHost2.SetChild(floatCtrl2);
            ((ISetLogicalParent)_popupHost2).SetParent(control);
            _popupHost2.ConfigurePosition(control, PlacementMode.Pointer, new Point(100, 10));
            _popupHost2.Show();

        }

        internal static void VPortKeyInput(string key)
        {
            if (_popupHost1 != null)
            {
                _vportInputer.Text += key;
                _vportInputer.CaretIndex = _vportInputer.Text.Length;
            }
        }

        internal static void HoverSelect()
        {
            var pw = ScrToWorld(PointerMovedPosition);
            var sizew = ScrToWorld(SelectBoxSize);
            var grip = RuntimeScene.HoverSelectGrip(pw, sizew);
            //如果没有悬停控制点，则判断有无悬停元素
            if (grip == null)
            {
                var element = DocumentScene.HoverSelectElement(LcDocument.Current.ModelSpace, pw, sizew);
                if (element != null)
                {
                    ShowHoveredElementTip(DrawingControl);
                }
            }
        }
        internal static void HoverUnselect()
        {
            RuntimeScene.HoverUnselectGrip();

            DocumentScene.HoverUnselectElement();
            if (HoveredElementTip != null)
            {
                HoveredElementTip.Hide();
            }
        }

        public static void ShowHoveredElementTip(Control control)
        {
            if (HoveredElementTip != null)
            {
                HoveredElementTip.Show();
                HoveredElementTip.ConfigurePosition(control, PlacementMode.Pointer, new Point(10, 10));
                return;
            }
            var floatCtrl2 = new TextBlock();
            floatCtrl2.Text = "HELLO2";
            floatCtrl2.Background = Brushes.LightBlue;
            HoveredElementTip = OverlayPopupHost.CreatePopupHost(control, null);
            HoveredElementTip.SetChild(floatCtrl2);
            ((ISetLogicalParent)HoveredElementTip).SetParent(control);
            HoveredElementTip.ConfigurePosition(control, PlacementMode.Pointer, new Point(10, 10));
            HoveredElementTip.Show();

        }

        public static ControlGrip HitSelectGrip()
        {
            var pw = ScrToWorld(PointerPressedPosition);
            var sizew = ScrToWorld(SelectBoxSize);
            var grip = RuntimeScene.HitSelectGrip(pw, sizew);
            if (grip == null) return null;

            VPort.IsDraggingGrip = true;
            return grip;

        }
        internal static void DraggingGripEnd()
        {
            var pw = ScrToWorld(PointerPressedPosition);
            RuntimeScene.DraggingGripEnd(pw);
        }
        internal static void DraggingGrip()
        {
            var pw = ScrToWorld(PointerMovedPosition);
            RuntimeScene.DraggingGrip(pw);
        }
    }

}
